﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;

namespace Eirene.Core.Model
{
	/// <summary>
	/// This is a collection that accepts only items, and exposes its members as Item types.
	/// </summary>
	/// <remarks>
	/// Implement a method to add items to the collection.
	/// </remarks>
	public class ItemContainer : ObservableCollection<Item>
	{
		#region Constructors

		public ItemContainer()
			: base()
		{ }

		public ItemContainer(IEnumerable<Item> items)
			: base()
		{
			if (items == null)
				throw new ArgumentNullException("items");

			if (items.Count() > 0)
			{
				foreach (var item in items)
				{
					ValidItem(item);
					base.InsertItem(base.Count, item);
				}
			}
		}

		#endregion

		#region Properties

		/// <summary>
		/// Gets or sets a base default owner. This is used for items with no owners.
		/// </summary>
		public Owner BaseDefaultOwner { get; set; }

		#endregion

		#region Methods

		/// <summary>
		/// Add a new item.
		/// </summary>
		public void Add(ref Item item)
		{
			if (item == null)
			{
				throw new ArgumentNullException("item", "Cannot be null or empty.");
			}

			SetDefaultOwner(item.Owners);

			this.Add(item);
		}

		/// <summary>
		///  Remove any duplicate owners before returning the list.
		/// </summary>
		/// <param name="list">List of owners.</param>
		/// <returns>List of owners with duplicates removed.</returns>
		public List<Owner> DistinctOwnerList(List<Owner> list)
		{
			var distinctList = list.Distinct();

			return distinctList.ToList();
		}

		/// <summary>
		/// Determines if there is already an item with this name in the list.
		/// </summary>
		/// <param name="itemName">The item name.</param>
		/// <returns>True if an item already exists with this name. Otherwise returns false.</returns>
		public bool ItemNameIsNotUnique(string itemName)
		{
			return this.Any(x => x.ItemName == itemName);
		}

		/// <summary>
		///  Check if a default owner has been assigned for the item. If owners exist for the item and none
		///  have been assigned as the default then make the first owner in the list the default. If there
		///  are no owners for the item then assign the base default owner.
		/// </summary>
		/// <param name="owners">List of owners.</param>
		/// <returns>List of owners with default owner set.</returns>
		public List<Owner> SetDefaultOwner(List<Owner> owners)
		{
            if (owners == null)
            {
                throw new ArgumentNullException("owners", "Cannot be null or empty.");
            }
            
            // Check for a default owner. If one does not exist, then make the first owner the default.
			if (owners.Count > 0)
			{
				if (owners.SingleOrDefault(i => i.IsDefault == true) == null)
				{
					owners[0].IsDefault = true;
				}
			}
			else
			{
				owners.Add(BaseDefaultOwner);
			}

			return owners;
		}

		#endregion

		#region Overrides

		protected override void InsertItem(int index, Item item)
		{
            if (item == null)
            {
                throw new ArgumentNullException("item", "Cannot be null or empty.");
            }

            SetDefaultOwner(item.Owners);

            ValidItem(item);

			base.InsertItem(index, item);
		}

		protected override void SetItem(int index, Item item)
		{
            if (item == null)
            {
                throw new ArgumentNullException("item", "Cannot be null or empty.");
            }
            
            ValidItem(item);

			base.SetItem(index, item);
		}

		#endregion

		#region Implementation

		/// <summary>
		/// Restricts to Item types, items that can be added to the collection.
		/// </summary>
		/// <param name="item">Item object to add.</param>
		protected virtual Item ValidItem(Item item)
		{
			if (item == null)
			{
				throw new ArgumentNullException("item", "cannot be null.");
			}

			if (String.IsNullOrWhiteSpace(item.ItemName))
			{
				throw new ArgumentException("item.ItemName", "cannot be empty or null.");
			}

			if (String.IsNullOrWhiteSpace(item.Colour))
			{
				throw new ArgumentException("item.Colour", "cannot be empty or null.");
			}

			if (item.Price <= 0)
			{
				throw new ArgumentException("item.Price", "must be greater than zero.");
			}

			if (item.Owners == null || item.Owners.Count == 0)
			{
				throw new ArgumentException("item.Owners", "cannot be empty or null.");
			}

			if (item.Status == null)
			{
				throw new ArgumentNullException("item.Status", "cannot be empty or null.");
			}

			if (item.DefaultOwner == null)
			{
				throw new ArgumentException("item.DefaultOwner", "cannot be null.");
			}

			if (this.SingleOrDefault(i => i.ItemName == item.ItemName) != null)
			{
				throw new ArgumentException("item.ItemName", "must be unique");
			}

			return item;
		}

		#endregion
	}
}
